package j2metris.ui;

import j2metris.Coordinate;
import j2metris.Direction;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.Graphics;

import j2metris.game.Game;
import java.util.Vector;
import javax.microedition.lcdui.Image;

public class GameScreen extends GameCanvas
{
    public GameScreen(
            boolean suppressKeyEvents,
            int initialRotationDelay,
            int mainRotationDelay,
            int initialTranslationDelay,
            int mainTranslationDelay)
    {
        super(suppressKeyEvents);
        clockwiseController = new PieceController(
                Direction.CLOCKWISE,
                initialRotationDelay,
                mainRotationDelay);
        counterclockwiseController = new PieceController(
                Direction.COUNTERCLOCKWISE,
                initialRotationDelay,
                mainRotationDelay);
        leftController = new PieceController(
                Direction.LEFT,
                initialTranslationDelay,
                mainTranslationDelay);
        rightController = new PieceController(
                Direction.RIGHT,
                initialTranslationDelay,
                mainTranslationDelay);
    }

    private class PieceMover implements Runnable
    {
        private final Direction direction;
        private final int initialDelay;
        private final int mainDelay;
        public boolean shouldContinue = true;

        public PieceMover(Direction directionParam, int initialDelayParam,
                int mainDelayParam)
        {
            direction = directionParam;
            initialDelay = initialDelayParam;
            mainDelay = mainDelayParam;
        }
        
        public void run()
        {
            try
            {
                Game.getInstance().moveActivePieceIfPossible(direction);
                Thread.sleep(initialDelay);
                while(shouldContinue)
                {
                    Game.getInstance().moveActivePieceIfPossible(direction);
                    Thread.sleep(mainDelay);
                }
            }
            catch(InterruptedException e)
            {
                return;
            }
        }
    }

    private class PieceController
    {
        private PieceMover mover;
        private final Direction direction;
        private Thread thread;
        private final int initialDelay;
        private final int mainDelay;

        public PieceController(Direction directionParam, int initialDelayParam,
                int mainDelayParam)
        {
            direction = directionParam;
            initialDelay = initialDelayParam;
            mainDelay = mainDelayParam;
            mover = new PieceMover(direction, initialDelay, mainDelay);
            thread = new Thread(mover);
        }

        public boolean isActive()
        {
            return mover.shouldContinue;
        }

        /**
         * If this <code>PieceController</code> is already active, it will simply
         * re-start the action. Otherwise, it will start it.
         */
        public void start()
        {
            if(thread.isAlive())
            {
                mover.shouldContinue = false;
                mover = new PieceMover(direction, initialDelay, mainDelay);
            }
            else
            {
                mover.shouldContinue = true;
            }
            thread = new Thread(mover);
            thread.start();
        }

        /**
         * If this <code>PieceController</code> is not active, this method will
         * have no effect at all. Otherwise, it will stop the action.
         */
        public void stop()
        {
            mover.shouldContinue = false;
        }
    }

    private final Graphics buffer = getGraphics();
    private final int distanceFromLeftEdgeOfScreenToLeftEdgeOfWell = 0;
    private final int distanceFromBottomOfScreenToBottomOfWell = 0;
    private final PieceController clockwiseController;
    private final PieceController counterclockwiseController;
    private final PieceController leftController;
    private final PieceController rightController;

    /**
     * Processes keypresses and reacts to any pertinent ones.
     */
    public void processAndReactToInput()
    {
        Game game = Game.getInstance();
        final int keyState = getKeyStates();

        if((keyState & GAME_A_PRESSED) != 0)
        {
            clockwiseController.stop();
            if(!counterclockwiseController.isActive())
                counterclockwiseController.start();
        }
        else if((keyState & GAME_B_PRESSED) != 0)
        {
            counterclockwiseController.stop();
            if(!clockwiseController.isActive())
                clockwiseController.start();
        }
        else
        {
            clockwiseController.stop();
            counterclockwiseController.stop();
        }

        if((keyState & LEFT_PRESSED) != 0)
        {
            rightController.stop();
            if(!leftController.isActive())
                leftController.start();
        }
        else
        {
            leftController.stop();
            if((keyState & RIGHT_PRESSED) != 0)
            {
                if(!rightController.isActive())
                    rightController.start();
            }
            else
                rightController.stop();
        }
        
        if((keyState & GAME_C_PRESSED) != 0)
        {
            game.putActivePieceIntoHoldBox();
        }
        if((keyState & FIRE_PRESSED) != 0)
        {
            game.dropActivePiece();
        }
    }

    /**
     * Redraws the entire game including the well and the active piece. Because
     * it redraws every single square, this method is slow and inefficient.
     */
    public synchronized void drawGame()
    {
        Game game = Game.getInstance();
//        Image[][] coordinates = new Image
//                [Game.getInstance().getWellWidth()]
//                [Game.getInstance().getWellHeight()];
        for(int i = 0; i < Game.getInstance().getWellWidth(); i++)
        {
            for(int ii = 0; ii < Game.getInstance().getWellHeight(); ii++)
            {
//                if(J2metrisApp.DEBUG)
//                    System.err.println("GETTING (" + i + ", " + ii + ")");
                if(game.getWell().getImage(i, ii) != null)
//                    coordinates[i][ii] = Image.createImage(well.getImage(i, ii));
                    Drawer.drawUnit(
                            buffer,
                            game.getWell().getImage(i, ii),
                            distanceFromLeftEdgeOfScreenToLeftEdgeOfWell,
                            distanceFromBottomOfScreenToBottomOfWell,
                            new Coordinate(i, ii),
                            game.getUnitWidth(),
                            game.getUnitHeight());
                else
                    Drawer.drawUnit(
                            buffer,
                            game.getBackgroundImage(),
                            distanceFromLeftEdgeOfScreenToLeftEdgeOfWell,
                            distanceFromBottomOfScreenToBottomOfWell,
                            new Coordinate(i, ii),
                            game.getUnitWidth(),
                            game.getUnitHeight());
            }
            Drawer.drawPiece(
                    buffer,
                    game.getActivePiece(),
                    distanceFromLeftEdgeOfScreenToLeftEdgeOfWell,
                    distanceFromBottomOfScreenToBottomOfWell,
                    game.getActivePiecePosition(),
                    game.getUnitWidth(),
                    game.getUnitHeight());
        }
        flushGraphics();
//        if(J2metrisApp.DEBUG) System.err.println("GAME DRAWN");
    }

    /**
     * Draws the game's currently active piece. It is optimized so that it does
     * not draw over previously-occupied spaces; thus, ensure that the piece
     * has already been drawn before using this method.
     *
     * @param currentPosition an array of <code>Coordinate</code>s describing
     * the active piece's current position
     * @param previousPosition an array of <code>Coordinate</code>s describing
     * the position that the active piece was at when it was last drawn
     */
    public synchronized void drawPiece(Coordinate[] previousPosition,
            Coordinate[] currentPosition)
    {
        Game game = Game.getInstance();
        Image activePieceImage = game.getActivePiece().getUnitImage();
        Image bgImage = game.getBackgroundImage();
        int unitWidth = game.getUnitWidth();
        int unitHeight = game.getUnitWidth();

        Vector locationsToDrawBackgroundOver = new Vector(previousPosition.length);
        for(int i = 0; i < previousPosition.length; ++i)
        {
            locationsToDrawBackgroundOver.addElement(previousPosition[i]);
        }

OUTER_POSITION_LOOP:
        for(int i = 0; i < currentPosition.length; ++i)
        {
            for(int ii = 0; ii < previousPosition.length; ++ii)
                if(currentPosition[i].equals(previousPosition[ii]))
                {
                    locationsToDrawBackgroundOver.removeElement(previousPosition[ii]);
                    continue OUTER_POSITION_LOOP;
                }

            Drawer.drawUnit(
                    buffer,
                    activePieceImage,
                    distanceFromLeftEdgeOfScreenToLeftEdgeOfWell,
                    distanceFromBottomOfScreenToBottomOfWell,
                    currentPosition[i],
                    unitWidth,
                    unitHeight);
        }
        for(int i = 0; i < locationsToDrawBackgroundOver.size(); ++i)
            Drawer.drawUnit(
                    buffer,
                    bgImage,
                    distanceFromLeftEdgeOfScreenToLeftEdgeOfWell,
                    distanceFromBottomOfScreenToBottomOfWell,
                    (Coordinate) locationsToDrawBackgroundOver.elementAt(i),
                    unitWidth,
                    unitHeight);

        flushGraphics();
    }
}